/**
 * Enhanced HTMLEditor to include Note Marking functionality.
 * @type 
 */


var Wikitools = {}
Wikitools.MyHTMLEditor = Ext.extend(Ext.form.HtmlEditor, {
	enableMarking : true,
	defaultFont: 'Courier New',
	
	//Extend the toolbar
	createToolbar : function(editor){
        var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
        
        function btn(id, toggle, handler){
            return {
                itemId : id,
                cls : 'x-btn-icon x-edit-'+id,
                enableToggle:toggle !== false,
                scope: editor,
                handler:handler||editor.relayBtnCmd,
                clickEvent:'mousedown',
                tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
                tabIndex:-1
            };
        }

        // build the toolbar
        var tb = new Ext.Toolbar({
            renderTo:this.wrap.dom.firstChild
        });

        // stop form submits
        tb.el.on('click', function(e){
            e.preventDefault();
        });

        if(this.enableFont && !Ext.isSafari2){
            this.fontSelect = tb.el.createChild({
                tag:'select',
                cls:'x-font-select',
                html: this.createFontOptions()
            });
            this.fontSelect.on('change', function(){
                var font = this.fontSelect.dom.value;
                this.relayCmd('fontname', font);
                this.deferFocus();
            }, this);
            tb.add(
                this.fontSelect.dom,
                '-'
            );
        };
        
        // font weight tools
        if(this.enableFormat){
            tb.add(
                btn('bold'),
                btn('italic'),
                btn('underline')
            );
        };

        // fond size tools
        if(this.enableFontSize){
            tb.add(
                '-',
                btn('increasefontsize', false, this.adjustFont),
                btn('decreasefontsize', false, this.adjustFont)
            );
        };
        
        // color tools
        if(this.enableColors){
            tb.add(
                '-', {
                    itemId:'forecolor',
                    cls:'x-btn-icon x-edit-forecolor',
                    clickEvent:'mousedown',
                    tooltip: tipsEnabled ? editor.buttonTips['forecolor'] || undefined : undefined,
                    tabIndex:-1,
                    menu : new Ext.menu.ColorMenu({
                        allowReselect: true,
                        focus: Ext.emptyFn,
                        value:'000000',
                        plain:true,
                        selectHandler: function(cp, color){
                            this.execCmd('forecolor', Ext.isSafari || Ext.isIE ? '#'+color : color);
                            this.deferFocus();
                        },
                        scope: this,
                        clickEvent:'mousedown'
                    })
                }, {
                    itemId:'backcolor',
                    cls:'x-btn-icon x-edit-backcolor',
                    clickEvent:'mousedown',
                    tooltip: tipsEnabled ? editor.buttonTips['backcolor'] || undefined : undefined,
                    tabIndex:-1,
                    menu : new Ext.menu.ColorMenu({
                        focus: Ext.emptyFn,
                        value:'FFFFFF',
                        plain:true,
                        allowReselect: true,
                        selectHandler: function(cp, color){
                            if(Ext.isGecko){
                                this.execCmd('useCSS', false);
                                this.execCmd('hilitecolor', color);
                                this.execCmd('useCSS', true);
                                this.deferFocus();
                            }else{
                                this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isSafari || Ext.isIE ? '#'+color : color);
                                this.deferFocus();
                            }
                        },
                        scope:this,
                        clickEvent:'mousedown'
                    })
                }
            );
        };

        // justify tools
        if(this.enableAlignments){
            tb.add(
                '-',
                btn('justifyleft'),
                btn('justifycenter'),
                btn('justifyright')
            );
        };
        
        // mark tools
        if(this.enableMarking){
        	tb.add(
        		'-',
        		btn('markasnote', false, this.markAsNote)
        	);
        };

        if(!Ext.isSafari2){
            if(this.enableLinks){
                tb.add(
                    '-',
                    btn('createlink', false, this.createLink)
                );
            };

            if(this.enableLists){
                tb.add(
                    '-',
                    btn('insertorderedlist'),
                    btn('insertunorderedlist')
                );
            }
            if(this.enableSourceEdit){
                tb.add(
                    '-',
                    btn('sourceedit', true, function(btn){
                        this.toggleSourceEdit(btn.pressed);
                    })
                );
            }
        }

        this.tb = tb;
    },
    
        buttonTips : {
        bold : {
            title: 'Bold (Ctrl+B)',
            text: 'Make the selected text bold.',
            cls: 'x-html-editor-tip'
        },
        italic : {
            title: 'Italic (Ctrl+I)',
            text: 'Make the selected text italic.',
            cls: 'x-html-editor-tip'
        },
        underline : {
            title: 'Underline (Ctrl+U)',
            text: 'Underline the selected text.',
            cls: 'x-html-editor-tip'
        },
        increasefontsize : {
            title: 'Grow Text',
            text: 'Increase the font size.',
            cls: 'x-html-editor-tip'
        },
        decreasefontsize : {
            title: 'Shrink Text',
            text: 'Decrease the font size.',
            cls: 'x-html-editor-tip'
        },
        backcolor : {
            title: 'Text Highlight Color',
            text: 'Change the background color of the selected text.',
            cls: 'x-html-editor-tip'
        },
        forecolor : {
            title: 'Font Color',
            text: 'Change the color of the selected text.',
            cls: 'x-html-editor-tip'
        },
        justifyleft : {
            title: 'Align Text Left',
            text: 'Align text to the left.',
            cls: 'x-html-editor-tip'
        },
        justifycenter : {
            title: 'Center Text',
            text: 'Center text in the editor.',
            cls: 'x-html-editor-tip'
        },
        justifyright : {
            title: 'Align Text Right',
            text: 'Align text to the right.',
            cls: 'x-html-editor-tip'
        },
        insertunorderedlist : {
            title: 'Bullet List',
            text: 'Start a bulleted list.',
            cls: 'x-html-editor-tip'
        },
        insertorderedlist : {
            title: 'Numbered List',
            text: 'Start a numbered list.',
            cls: 'x-html-editor-tip'
        },
        createlink : {
            title: 'Hyperlink',
            text: 'Make the selected text a hyperlink.',
            cls: 'x-html-editor-tip'
        },
        sourceedit : {
            title: 'Source Edit',
            text: 'Switch to source editing mode.',
            cls: 'x-html-editor-tip'
        },
        markasnote : {
            title: 'Mark as Note',
            text: 'Mark the selected sentences as a note.',
            cls: 'x-html-editor-tip'
        }
    },
    
    

    
    markAsNote : function(btn){
    	//Get the selected content
    	//TODO: IE not work with this selection
    	var t = this;
    	// Prompt for note content input.
        Ext.MessageBox.show({
           title: 'New note',
           msg: 'Note Content:',
           width:300,
           buttons: Ext.MessageBox.OKCANCEL,
           multiline: true,
           scope: this, // keep scope in MyHTMLEditor
           fn: this.saveNote, // saveNode to continure marking.
           animEl: 'mb3'
        });
        
        
        
        
    },
    
    /**
     * Save the node to server via Ajax.
     * On success, further continue to mark the selected nodes.
     * @param {} btn The button selected when inputting note
     * @param {} text The note text
     */
    saveNote: function(btn, text){
    	var xx = this;
        if(btn == 'ok'){
            new Ext.data.Connection().request({
                url: 'CreateNote',
                params: {content: text},
                method: 'POST',
                scope: this,
                failure: function(response, options){
                    Ext.example.msg('Failure', 'One note is created unsuccessfully');
                },
                success: function(response, options){
                    Ext.example.msg('Success', 'One note is created successfully');
                    // a node saved , mark it on the page.
                    // TODO: update the marked article asynchronuously.
                    this.onNoteSaved(response, options);
                }                
            });
            
        }else{
            Ext.example.msg('Cancelled', 'You cancelled the note');
        }
    },
    
    /**
     * When a new note is saved, 
     * get the generated note id from server
     * and continure marking.
     * @param {} response Response XHR object with note info
     * @param {} options ....
     */
    onNoteSaved: function(response, options){
    	var x = response;
    	//ft
    	var y = Ext.get(response.responseXML.documentElement);
    	var z = y.first("id");
    	//ft2
    	var noteid = z.dom.firstChild.nodeValue;
    	       
    	this.parseSelectionTree(noteid);
    },
    
    /**
     * Cross Browser getSelection
     * @return {}
     */
    getSelection: function() {
        var ifrm = this.getWin();
        var doc = this.getDoc();
        var sel = null;
        if(ifrm.getSelection){
            sel = ifrm.getSelection();
        }
        else if (doc.getSelection) {
            sel = doc.getSelection();
        }
        else if (doc.selection) {
            sel = doc.selection;
        }
        return sel;
    },
    

    /**
     * Cross Browser getRange function
     * @param {} selectionObject a object from this.getSelection()
     * @return {}
     */    
    getRangeObject: function(selectionObject) {
        if (selectionObject.getRangeAt)
            return selectionObject.getRangeAt(0);
        else { // Safari!
            var range = document.createRange();
            range.setStart(selectionObject.anchorNode,selectionObject.anchorOffset);
            range.setEnd(selectionObject.focusNode,selectionObject.focusOffset);
            return range;
        }
    },
    
    
    /**
     * Parse the SelectionTree and mark those notes elements.
     * @param {} noteid the id of the Note.
     */
    parseSelectionTree: function(noteid) {
    
		var sel = this.getSelection();
		var range = this.getRangeObject(sel);	
        
		// Get element of range
		var tag = this.getTag(range);
        
        var start = range.startContainer;
        var end = range.endContainer;
        
        var ancestor = range.commonAncestorContainer;
        
        
        var tree1 = this.getParentTree(start);
        var len1 = tree1.length;
        
        var tree2 = this.getParentTree(end);
        var len2 = tree2.length;
        
        var treeComm = this.getParentTree(ancestor);
        var lenComm = treeComm.length;
        var markNodes = new Array();
        
        // Get the nodes direct child to ancestor.
        if (start == end){
            markNodes[0] = start;
            ancestor = this.getParent(ancestor);
        }else if(len1 > lenComm && len2 > lenComm){
            var startMarkNode = tree1[len1 - lenComm -1];
            var endMarkNode = tree2[len2 - lenComm -1];
            var curMarkNode = startMarkNode;
            
            var ii = 0;		
            while(curMarkNode != endMarkNode ) {
                markNodes[ii] = curMarkNode;
                curMarkNode = curMarkNode.nextSibling;			
                ii++;
            }
            markNodes[ii] = endMarkNode;
        }
        

        // Surround all marked nodes with a span with an id.
        var holderspan = document.createElement("span");
        //TODO: id to be gathered by from the server or have a javacript global to manage the id.
        var holderId = noteid;
        holderspan.setAttribute('id', holderId);
        Ext.get(holderspan).addClass("note-holder");
        
        ancestor.insertBefore(holderspan, markNodes[0]);
        
        // Mark all nodes.
        for(var i =0; i< markNodes.length; i++){
            holderspan.appendChild(markNodes[i]);
            if(markNodes[i].nodeType == 3){
                this.markTextNode(markNodes[i], range, "note-simple");
            }else{
                this.markNoneTextNode(markNodes[i],"note-simple");
            }
        }
        
    },
    
    
    /**
     * Mark none text nodes with note mark.
     * @param {} node
     * @param {} classToAdd
     */
    markNoneTextNode: function(node, classToAdd){
        var par = this.getParent(node);
        var newnode = document.createElement("span");
        //~ newnode.setAttribute('style', "color:teal;");
        Ext.get(newnode).addClass(classToAdd);
        par.insertBefore(newnode, node);
        newnode.appendChild(node);
        //~ par.replaceChild(newnode, node);
    },
        
    /**
	 * Mark a text node with <span> for further note operations.
	 *
	 * @param {Range} range Current range
	 */
    markTextNode: function(anode, range, classToAdd){
        if(anode.nodeType == 3){            
            var start = range.startContainer;
            var end = range.endContainer;
            var adata = '';
                
            if(anode.nodeValue){
                adata = anode.nodeValue;
            }else if(anode.textContent){
                adata = anode.textContent;
            }
            
            var pos = range.endOffset;
            var len = adata.length;            
        
            var aspan = document.createElement("span");
            //~ aspan.setAttribute('style', "color:teal;");
            Ext.get(aspan).addClass(classToAdd);
            
            var par = anode.parentNode;
            
            //if the selected node is both start and end. (So that there are fragments at both start and end.)
            if(anode == start && anode == end){       
                var startpos = range.startOffset;
                var endpos = range.endOffset;
                aspan.innerHTML = adata.substring(startpos, endpos);             
                
                var newtext1 = document.createTextNode(adata.substring(0, startpos));           
                var newtext2 = document.createTextNode(adata.substring(endpos, len));
                
                
                par.insertBefore(newtext1, anode);
                par.insertBefore(aspan, anode);
                par.insertBefore(newtext2, anode);
                
            }else if(anode == start){ // if the selected node is start but not end
                
                aspan.innerHTML = adata.substring(pos, len);  
                
                var newtext = document.createTextNode(adata.substring(0, pos));
                par.insertBefore(newtext, anode);
                par.insertBefore(aspan, anode);               
                
            }else if(anode == end){ // if the selected node is end but not start
                
                aspan.innerHTML = adata.substring(0,pos);
                                
                var newtext = document.createTextNode(adata.substring(pos,len));
                
                par.insertBefore(aspan, anode);
                
                par.insertBefore(newtext, anode);
            }else{ // a inner node without fragments.
                aspan.innerHTML = adata;
                par.insertBefore(aspan, anode);
            }
            
            // remove the original node.
            par.removeChild(anode);
            
            
        }
    },
    
    /**
     * From current node, get the list of 
     * parent nodes up to the HTML element
     * @param {} node current node
     * @return {} a list of parent nodes
     */
    getParentTree: function(node) {
		// get parent of element
		var par = this.getParent(node);
		// init the tree as array with the current selected element
		var nodeTree = new Array(node);
		// get all parent nodes
		var ii = 1;
		
		while(par != null && par.nodeName != "#document") {
			nodeTree[ii] = par;
			par = this.getParent(par);			
			ii++;
		}
		
		return nodeTree;
	},
    
	/**
	 * Not implemented yet. A post sequence tree walker
	 * with start and end node specified.
	 * @param {} root
	 * @param {} startNode
	 * @param {} endNode
	 * @param {} actionOnNode
	 */
    traverseNodeTree: function(root, startNode, endNode, actionOnNode){
        alert('haha');
    },
     
     /**
	 * Returns the node structure of the current selection as array
	 * 
	 * @param {String} n The editor identifier (the textarea's ID)
	 */
	getNodeTree: function() {
		
		var sel = this.getSelection();
		var range = this.getRangeObject(sel);	
			
		// get element of range
		var tag = this.getTag(range);
		if(tag == null) { return; }
		// get parent of element
		var node = this.getParent(tag);
		// init the tree as array with the current selected element
		var nodeTree = new Array(tag);
		// get all parent nodes
		var ii = 1;
		
		while(node != null && node.nodeName != "#document") {
			nodeTree[ii] = node;
			node = this.getParent(node);			
			ii++;
		}
		
		return nodeTree;
	},
    
    /**
	 * Get the parent node of the given node
	 * 
	 * @param {DOMElement} element - Element which parent will be returned
	 */
	getParent: function(element) {
		if(element.parentNode) {
			return element.parentNode;
		}
		return null;
	},
    
    /**
	 * Get the acutally tag of the given range
	 *
	 * @param {Range} range Current range
	 */
	getTag: function(range) {
		try {
		    if(!Ext.isIE) {
				var node = range.startContainer;	
				var pos = range.startOffset;
				if(node.nodeType != 3) { node = node.childNodes[pos]; }
				
				if(node.nodeName && node.nodeName.search(/#/) != -1) {
					return node.parentNode;
				}
				return node;
			}
			else {
				if(range.length > 0) {
					return range.item(0);
				}
				else if(range.parentElement()) {
					return range.parentElement();
				}
			}
			return null;
		}
		catch(e) {
			return null;
		}
	},
    
    debug: function(msg){
        Ext.MessageBox.alert("debug", msg);
    }


});

Ext.reg('myhtmleditor', Wikitools.MyHTMLEditor);